package gc3d.griddata;

import gc3d.database.GridPovDB;
import gc3d.ftp.GridPovFTP;
import gc3d.gridspring.ProcReceiver;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.UUID;

import org.apache.commons.net.io.Util;


/**
 * @author Pierrick
 * @see java.util.UUID
 */
public class GridData implements Serializable, Cloneable {
	private static final long serialVersionUID = 1L;
	private GridPovData data;
	private GridPovFTP  ftp;
	private GridPovDB   db;
	private String taskID;
	private String localpath;
	private String remotepath;

	/**
	 * Init GridPovData & GridPovFTP with the passed params
	 * Generate a new random UUID as job ID (used for directory)
	 * @throws IOException ftp error
	 * @throws SQLException sql create task error
	 * @see GridPovData
	 * @see GridPovFTP
	 * @see java.util.UUID#randomUUID()
	 */
	public GridData(GridPovData gpd, GridPovFTP gpftp, GridPovDB gpdb, byte [] bindata) throws SQLException, IOException {
		// parent Data not splited yet, no framenb
		this.data = gpd;
		this.taskID = UUID.randomUUID().toString();
		this.ftp = gpftp;
		this.db  = gpdb;
		this.localpath = null;
		this.remotepath = "GridPovTemp" + '/' + this.taskID;
		// remote process
		try {
			this.ftp.mkdirs(remotepath);
		} catch (IOException e) {
			throw new IOException("[FTP mkdirs] "+e.getMessage());
		}
		this.writeData(bindata);
		this.db.createTask(this.taskID, this.data.getFileName());
	}

	private void writeData(byte [] bindata) throws IOException {
		String local_ftp_home = System.getenv("FTP_HOME");
		if (local_ftp_home == null) {
			throw new IOException("[ERROR] set/export FTP_HOME=/path/to/folder/ftp/home !");
		}
		//if ((tmp.length * data.getTotalFrames()) > Runtime.getRuntime().freeMemory()) {
		String local_ftp_dir = local_ftp_home 
			+ File.separatorChar + "GridPovTemp" 
			+ File.separatorChar + taskID 
			+ File.separatorChar + data.getFileName();
		// store tmp
		FileOutputStream fos = new FileOutputStream(local_ftp_dir);
		ByteArrayInputStream bais = new ByteArrayInputStream(bindata);
		Util.copyStream(bais, fos);
	}

	/**
	 * call by each nodes
	 * @return the local directory where are the files
	 * @throws IOException
	 */
	public File writeLocal() throws IOException {
		File taskdirfile = new File(System.getProperty("java.io.tmpdir") 
				+ File.separatorChar + "GridPovTemp" 
				+ File.separatorChar + taskID 
				+ File.separatorChar + data.getFrameNumber());
		taskdirfile.mkdirs();

		this.localpath = taskdirfile.getAbsolutePath();
		String local_file_path = localpath + File.separatorChar + data.getFileName();
		System.out.println("[INFO ] getFile("+local_file_path+','+remotepath+'/'+data.getFileName()+')');
		File local_file = new File(local_file_path);
		ftp.getFile(local_file_path, remotepath+'/'+data.getFileName(), 8);
		FileInputStream fis = new FileInputStream(local_file);
		data.write(localpath, fis);
		fis.close();

		return taskdirfile;
	}

	/**
	 * call by each nodes, Send the PNG to the FTP
	 * @return true if the file is sent, false otherwise
	 * @throws IOException file not found, or socket, or connect
	 * @see GridPovFTP#storeFileWRetry(String, String, java.io.InputStream, int)
	 */
	public boolean writeRemotePNG() throws IOException {
		String pngname  = data.getPNGName();

		return ftp.storeFile(localpath 
				+ File.separatorChar + pngname, 
				remotepath + '/' + pngname, 8);
	}

	/**
	 * called by the task spliter (servlet side)
	 * @param task the frame number
	 * @return a clone of this instance, 
	 *   with the framenumber initialized
	 * @throws SQLException 
	 * @see GridData#clone()
	 */
	public GridData cloneWithTaskNb(int task) {
		GridData clone = this.clone();
		clone.data.setFrameNumber(task);
		this.db.createJob(this.taskID, task);

		return clone;
	}

	/**
	 * @see java.lang.Object#clone()
	 */
	public GridData clone() {
		GridData cloneGridData = null;
		try {
			cloneGridData = (GridData) super.clone();
			cloneGridData.data  = this.data.clone();
			cloneGridData.ftp   = this.ftp.clone();
			cloneGridData.db    = this.db.clone();
			cloneGridData.taskID = new String(this.taskID);
		} catch (CloneNotSupportedException cnse) {
			cnse.printStackTrace();
		}
		return cloneGridData;
	}

	/**
	 * @return String INI file name
	 */
	public String getININame() {
		return data.getININame();
	}

	/**
	 * @return int number of frame
	 */
	public int getNumberOfFrames() {
		return data.getTotalFrames();
	}

	/**
	 * @return String remotepath
	 */
	public String getRemotePath() {
		return this.remotepath;
	}

	/**
	 * @param host the ftp host (DNS or IP)
	 */
	public void setHostFTP(String host) {
		ftp.setHost(host);
	}

	/**
	 * @return String POV-Ray file name
	 */
	public String getFileName() {
		return data.getFileName();
	}

	public String getLink() {
		return ftp.getLink()+remotepath+'/';
	}

	public String getTaskID() {
		return this.taskID;
	}

	/**
	 * DB
	 * @param node_id UUID of the Node
	 */
	public void startJob(String node_id) {
		this.db.startJob(this.taskID, this.data.getFrameNumber(), node_id);
	}

	public void updateJobStatus(String newstatus) {
		this.db.updateJobStatus(this.taskID, this.data.getFrameNumber(), newstatus);
	}

	public void finishJob() {
		this.db.finishJob(this.taskID, this.data.getFrameNumber(), ftp.getLink()+remotepath+'/'+data.getPNGName());
	}

	public void updateTaskStatus(String newstatus) {
		this.db.updateTaskStatus(this.taskID, newstatus);
	}

	public void finishTask(String videoname) {
		this.db.finishTask(taskID, ftp.getLink()+remotepath+'/'+videoname);
	}

	/**
	 * Download and unzip MegaPOV
	 * @return path to megapov.zip in temp directory
	 * @throws IOException
	 * @throws InterruptedException
	 * @see GridPovFTP#getFile(String, String)
	 * @see ZipUtils#unzipArchive(File, File)
	 */
	public synchronized String getMegaPOVPath() throws IOException, InterruptedException {
		String basepath = System.getProperty("java.io.tmpdir") 
			+ File.separatorChar + "GridPov" 
			+ File.separatorChar + "megapov";
		File megapovdir = new File(basepath);
		File povbin = null;
		String povzipftppath = null;
		if (File.separatorChar == '\\') {
			povzipftppath = "/GridPov/megapov-win.zip";
			povbin = new File(basepath 
					+ File.separatorChar + "megapov.exe");
		} else {
			povzipftppath = "/GridPov/megapov-lin.zip";
			povbin = new File(basepath 
					+ File.separatorChar + "megapov");			
		}
/**
TODO need better synchronization ! (for windows)
file with time in it (or in name)
sleep 100
if my time is smaller, do it,
then sleep till megapov is here !
*/
		if (megapovdir.exists()) {
			// give time to download
			while (!povbin.exists()) {
				Thread.sleep(500);
			}
			// give time to UnZip (3 sec)
			Thread.sleep(3000);
		} else {
			megapovdir.mkdirs();
			File megapovzip = new File(basepath + File.separatorChar + "megapov.zip");
			try {
				System.out.println("[INFO ]["+this+"].getMegaPOVPath ("+data.getFrameNumber()+") mkdirs "+megapovdir.getAbsolutePath());

				System.out.println("[INFO ]["+this+"].getMegaPOVPath ftp.getFile");
				ftp.getFile(megapovzip.getAbsolutePath(), povzipftppath, 8);

				System.out.println("[INFO ]["+this+"].getMegaPOVPath ZipUtils.unzipArchive");
				// UnZip megapov.zip here:
				ZipUtils.unzipArchive(megapovzip, megapovdir);
				// chmod Linux
				if (File.separatorChar != '\\') {
					ProcessBuilder pb = new ProcessBuilder("chmod", "+x", "megapov");
					pb.directory(megapovdir);
					Process p = pb.start();
					// we have to read the buffer
					new Thread(new ProcReceiver(p.getInputStream())).start();
					if (p.waitFor() != 0) { // should be 0
						System.err.println("`chmod +x megapov` failed");
					}
				}
			} finally {
				if (megapovzip.exists()) {
					System.out.println("[INFO ]["+this+"] deletting megapov.zip");
					megapovzip.delete();
				}
				if (!povbin.exists()) {
					System.err.println(this+" error getting megapov");
					System.err.println(this+" deletting megapov dir");
					megapovdir.delete();
				}
			}
		}

		return povbin.getAbsolutePath();
	}
}
